package com.example.jetpacktest.coroutine

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import org.junit.Test
import java.lang.Exception
import kotlin.coroutines.CoroutineContext
import kotlin.system.measureTimeMillis

class Test002 {

    @Test
    fun test001() = runBlocking { // this: CoroutineScope

        launch {
            delay(200L)
            println("Task from runBlocking")
        }

        //coroutineScope内部的协程体和子协程都完成了，才会接着向下执行，
        //也就是说 println("Coroutine scope is over")必须等到coroutineScope内部的协程体和子协程都完成了，才会执行
        //但是coroutineScope不阻塞线程，因此上面的launch启动的协程里面的还是能够打印
        //wait for their body and all its children to complete
        /*coroutineScope { // Creates a coroutine scope
            launch {
                delay(5000L)
                println("Task from nested launch")
            }

            delay(100L)
            println("Task from coroutine scope") // This line will be printed before the nested launch
        }*/

        runBlocking {
            launch {
                delay(5000L)
                println("Task from nested launch")
            }

            delay(100L)
            println("Task from coroutine scope") // This line will be printed before the nested launch
        }


        println("Coroutine scope is over") // This line is not printed until the nested launch completes
    }


    @Test
    fun test002() {

        runBlocking {
            coroutineScope { // Creates a coroutine scope
                launch {
                    delay(5000L)
                    println("Task from nested launch")
                }

                delay(100L)
                println("Task from coroutine scope") // This line will be printed before the nested launch
            }
        }

        GlobalScope.launch {
            /*coroutineScope { // Creates a coroutine scope
                launch {
                    delay(5000L)
                    println("Task from nested launch")
                }

                delay(100L)
                println("Task from coroutine scope") // This line will be printed before the nested launch
            }*/

            runBlocking {
                launch {
                    delay(5000L)
                    println("Task from nested launch")
                }

                delay(100L)
                println("Task from coroutine scope") // This line will be printed before the nested launch
            }

        }
        println("Coroutine scope is over")
    }

    @Test
    fun test003(){
        runBlocking {
            repeat(100_000) { // launch a lot of coroutines
                launch {
                    delay(1000L)
                    print(".")
                }
            }
        }
    }


    @Test
    fun test004(){
        val deferredList = (1..100000).map { n ->
            GlobalScope.async {
                delay(1000)
                n
            }
        }
        runBlocking {
            var count: Long = 0L
            deferredList.forEach { it ->
                count += it.await().toLong()
            }
            println(count)
        }
    }



    @Test
    fun test005(){
        runBlocking {
            val channel: Channel<String> = Channel<String>()

            //协程A
            launch {
                channel.send("A1")
                log("A1 done")
                channel.send("A2")
                log("A2 done")
            }

            //协程B
            launch {
                channel.send("B")
                log("B done")
            }

            //协程C
            launch {
                repeat(3){
                    val x = channel.receive()
                    log(x)
                }
            }

        }
    }

    fun log(msg: Any?){
        println("[${Thread.currentThread().name}].... $msg")
    }


    @Test
    fun test006(){
        runBlocking {

            val job = launch {
                repeat(100){
                    delay(500)
                    println("job:  work at $it")
                }
            }

            delay(2000)
            println("main: i am tired, cancel the job")
            job.cancel()
            //job.join()

            delay(5000)
            println("main: now i am quit")
        }
    }


    @Test
    fun test007(){
        runBlocking {
            val job = launch(Dispatchers.IO){
                var printTime: Long = System.currentTimeMillis()
                var i: Int = 0
                while(isActive){
                    if(System.currentTimeMillis() >= printTime){
                        println("job print $i")
                        i++
                        printTime += 1000
                    }
                }
            }

            println("main: delay()之前")
            delay(3010)
            println("main: i am tired, cancel the job")
            job.cancelAndJoin()
            println("main: now i am quit")

        }
    }


    @Test
    fun test008(){
        runBlocking {
            try{
                withTimeout(1000){
                    repeat(100){
                        delay(200)
                        println("...$it")
                    }
                }
            }catch (ex: TimeoutCancellationException){
                println("$ex")
            }
        }
    }


    @Test
    fun test009(){
        runBlocking {
            val time = measureTimeMillis {
                val one = doSomethingUsefulOne()
                val two = doSomethingUsefulTwo()
                println("The answer is ${one + two}")
            }
            println("Completed in $time ms")
        }
    }


    @Test
    fun test010(){
        runBlocking {
            val time = measureTimeMillis {
                val one = async {
                    println("one....thread = ${Thread.currentThread()}")
                    doSomethingUsefulOne()
                }
                val two = async {
                    println("two....thread = ${Thread.currentThread()}")
                    doSomethingUsefulTwo()
                }
                println("result = ${one.await() + two.await()}")
            }
            println("time = $time")
        }
    }


    @Test
    fun test011(){
        runBlocking {
            try{
                val res = failedSum1()
                println(res)
            }catch (ex: Exception){
                println("test011...$ex")
            }

        }
    }

    @Test
    fun test012(){
        runBlocking {
            launch(Dispatchers.Unconfined) { // not confined -- will work with main thread
                println("Unconfined      : I'm working in thread ${Thread.currentThread().name}")
                delay(500)
                println("Unconfined      : After delay in thread ${Thread.currentThread().name}")
            }
            launch { // context of the parent, main runBlocking coroutine
                println("main runBlocking: I'm working in thread ${Thread.currentThread().name}")
                delay(1000)
                println("main runBlocking: After delay in thread ${Thread.currentThread().name}")
            }
        }
    }


    @Test
    fun test013(){
        newSingleThreadContext("Ctx1").use { ctx1 ->
            newSingleThreadContext("Ctx2").use { ctx2 ->
                runBlocking(ctx1) {
                    log("Started in ctx1")
                    withContext(ctx2) {
                        log("Working in ctx2")
                    }
                    log("Back to ctx1")
                }
            }
        }
    }



    @Test
    fun test014(){
        runBlocking {
            val outContext = coroutineContext
            println("outer = ${coroutineContext.hashCode()}")
            launch {
                val inner1 = coroutineContext
                println("inner1 = ${coroutineContext.hashCode()}")
                println("${outContext == coroutineContext}")
                println("job1 = ${coroutineContext.job}")
                delay(1000)
            }
            launch {
                val inner2 = coroutineContext
                println("inner2 = ${coroutineContext.hashCode()}")
                println("job2 = ${coroutineContext.job}")
                delay(800)
            }
            delay(100)
            println("children = ${coroutineContext.job.children.toList()}")
        }
    }


    @Test
    fun test015(){
        runBlocking {
            val myContext: CoroutineContext = Job()
            // launch a coroutine to process some kind of incoming request
            val request = launch {
                // it spawns two other jobs
                launch(Job()) {
                    println("job1: I run in my own Job and execute independently!")
                    delay(1000)
                    println("job1: I am not affected by cancellation of the request")
                }
                // and the other inherits the parent context
                launch {
                    delay(100)
                    println("job2: I am a child of the request coroutine")
                    delay(1000)
                    println("job2: I will not execute this line if my parent request is cancelled")
                }
            }
            delay(500)
            request.cancel() // cancel processing of the request
            delay(1000) // delay a second to see what happens
            println("main: Who has survived request cancellation?")
        }
    }



    private suspend fun failedSum1() : Int{
        return coroutineScope {

            //任务1
            val one = async<Int> {
                try{
                    println("task1....start")
                    delay(1000 * 1000)
                    42
                }catch (ex: Exception){
                    println("task1....$ex")
                    -13
                }
                finally {
                    println("task1....finished")
                }
            }

            //任务2
            val two = async<Int> {
                println("task2....start")
                throw Exception("task2...计算出现了错误")
            }

            one.await() + two.await()
        }
    }






    suspend fun doSomethingUsefulOne(): Int {
        delay(1000L) // pretend we are doing something useful here
        return 13
    }

    suspend fun doSomethingUsefulTwo(): Int {
        delay(1000L) // pretend we are doing something useful here, too
        return 29
    }




}